package com.yuantu.judiciary.serviceimpl;

import cn.hutool.core.codec.Base64Encoder;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.yuantu.judiciary.common.CodeEnum;
import com.yuantu.judiciary.common.SysCode;
import com.yuantu.judiciary.common.SysOnOffState;
import com.yuantu.judiciary.dao.*;
import com.yuantu.judiciary.exception.ServiceException;
import com.yuantu.judiciary.exception.enums.*;
import com.yuantu.judiciary.model.api_ali.AlipayUserInfoShareResponseDTO;
import com.yuantu.judiciary.model.api_wx.GetUserInfoParam;
import com.yuantu.judiciary.model.api_wx.UserOpenIdInfoDTO;
import com.yuantu.judiciary.model.api_wx.UserPhoneInfoDTO;
import com.yuantu.judiciary.model.domain.*;
import com.yuantu.judiciary.model.dto.*;
import com.yuantu.judiciary.model.enums.*;
import com.yuantu.judiciary.model.form.*;
import com.yuantu.judiciary.model.vo.*;
import com.yuantu.judiciary.service.*;
import com.yuantu.judiciary.utils.AssertUtil;
import com.yuantu.judiciary.utils.JwtUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import java.awt.image.BufferedImage;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户信息
 *
 * @author syw
 * @since 2023-08-03
 **/
@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl implements IUserService {

    private final IAccountDao accountDao;
    private final IOrganizationDao organizationDao;
    private final IUserInfoDao userInfoDao;
    private final ISmsService smsService;
    private final PasswordEncoder passwordEncoder;
    private final IApiWxAppletService wxAppletApiService;
    private final IOrgVipRecordDao orgVipRecordDao;
    private final IQueryRecordDao queryRecordDao;
    private final IInviteCodeDao inviteCodeDao;
    private final IInviteRecordDao inviteRecordDao;
    private final ISysOperLogDao sysOperLogDao;
    private final IInviteCodeService inviteCodeService;
    private final IApiAliAppletService aliAppletService;
    private final IOperatorService operatorService;
    private final IOrganizationSourceDao organizationSourceDao;
    private final ISysFileDao sysFileDao;
    private final IPersonRemoveDao personRemoveDao;
    private final ISmsRecordDao smsRecordDao;
    private final IVipRecordAppletDao vipRecordAppletDao;
    private final IVipInfoDao vipInfoDao;
    private final IVipOrderDao vipOrderDao;
    private final IAgentService agentService;

    private static final String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token";
    private static final String URL_SCHEME = "https://api.weixin.qq.com/wxa/generatescheme";
    private static String APPID = "wxeccb8103bee7b465";
    private static String SECRET = "991d6d8f3292bd348e65bf25d09cd4f1";

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccountInfoDTO getUserAccountInfoByApp(LoginAppForm form) {
        if (!(form.getCode().equals("0000") && form.getPhone().equals("18800000001"))) {
            AssertUtil.assertTrue(smsService.verifyCode(new VerifyCodeForm(form.getPhone(), form.getCode())), SmsExceptionEnum.SMS_VERIFY_FAILURE);
        }
        UserInfoDO userInfo = userInfoDao.findByPhoneAndDeletedFalse(form.getPhone());
        if (ObjectUtils.isEmpty(userInfo)) {
            userInfo = new UserInfoDO();
            userInfo.setPhone(form.getPhone());
            userInfoDao.save(userInfo);
        }

        AccountDO account;
        List<AccountDO> accounts = accountDao.findByUserIdAndTypeInAndDeletedFalse(userInfo.getId(), Arrays.asList(UserType.PRIMARY, UserType.SUB, UserType.NONE));
        if (CollectionUtils.isEmpty(accounts)) {
            account = new AccountDO();
            account.setUsername(form.getPhone());
            account.setUserId(userInfo.getId());
            account.setState(SysOnOffState.ON);
            account.setType(UserType.NONE);
            account.setOrigin(EndpointType.APP);
            account.setOpenId(IdUtil.fastSimpleUUID());
            accountDao.save(account);
        } else {
            List<Long> accountIds = accounts.stream().map(AccountDO::getId).distinct().collect(Collectors.toList());
            SysOperLogDO latestLog = sysOperLogDao.findFirstByCreatorIdInAndDeletedFalseOrderByIdDesc(accountIds);
            if (ObjectUtils.isEmpty(latestLog)) {
                accounts.sort(Comparator.comparing(AccountDO::getId).reversed());
                account = accounts.get(0);
            } else {
                account = accounts.stream().filter(f -> f.getId().equals(latestLog.getCreatorId())).collect(Collectors.toList()).get(0);
            }
        }
        if (UserType.NONE.equals(account.getType()) && account.getOrigin() == null) {
            account.setOrigin(EndpointType.APP);
            accountDao.save(account);
        }
        return getAccountInfo(account);
    }

    @Override
    public AccountInfoDTO getUserAccountInfoByWeb(LoginAppForm form) {
        if (!(form.getCode().equals("0000") && form.getPhone().equals("18800000001"))) {
            AssertUtil.assertTrue(smsService.verifyCode(new VerifyCodeForm(form.getPhone(), form.getCode())), SmsExceptionEnum.SMS_VERIFY_FAILURE);
        }
        UserInfoDO userInfo = userInfoDao.findByPhoneAndDeletedFalse(form.getPhone());
        AssertUtil.assertNotNull(userInfo, UserExceptionEnum.USER_NOT_REGISTER);
        List<AccountDO> accounts = accountDao.findByUserIdAndTypeInAndDeletedFalse(userInfo.getId(), Arrays.asList(UserType.PRIMARY, UserType.SUB));
        AssertUtil.assertTrue(!CollectionUtils.isEmpty(accounts), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        //限制企业账号登录
        List<Long> organizationIds = accounts.stream().map(AccountDO::getOrganizationId).collect(Collectors.toList());
        List<Long> enterIds = organizationDao.findByIdInAndDeletedFalse(organizationIds).stream().
                filter(s -> s.getType() == OrganizationType.ENTER).map(OrganizationDO::getId).collect(Collectors.toList());
        AssertUtil.assertTrue(!CollectionUtils.isEmpty(enterIds), UserExceptionEnum.USER_NOT_REGISTER);
        accounts = accounts.stream().filter(s -> enterIds.contains(s.getOrganizationId())).collect(Collectors.toList());
        List<Long> accountIds = accounts.stream().map(AccountDO::getId).distinct().collect(Collectors.toList());
        SysOperLogDO latestLog = sysOperLogDao.findFirstByCreatorIdInAndDeletedFalseOrderByIdDesc(accountIds);
        AccountDO account;
        if (ObjectUtils.isEmpty(latestLog)) {
            accounts.sort(Comparator.comparing(AccountDO::getId).reversed());
            account = accounts.get(0);
        } else {
            account = accounts.stream().filter(f -> f.getId().equals(latestLog.getCreatorId())).collect(Collectors.toList()).get(0);
        }
        return getAccountInfo(account);
    }

    private AccountInfoDTO getAccountInfo(AccountDO account) {
        AccountInfoDTO res = new AccountInfoDTO();
        res.setAccountId(account.getId());
        res.setUsername(account.getUsername());
        res.setOrganizationId(account.getOrganizationId());
        if (!ObjectUtils.isEmpty(account.getOrganizationId())) {
            OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(account.getOrganizationId());
            res.setType(organization.getType());
        }
        return res;
    }

    @Override
    public AccountInfoDTO getUserAccountInfo(LoginForm form) {
        AccountDO account = null;
        List<AccountDO> accounts = accountDao.findAccountByUsernameAndTypeAndStateAndDeletedFalse(form.getUsername(), UserType.ADMIN, SysOnOffState.ON);
        AssertUtil.assertTrue(!CollectionUtils.isEmpty(accounts), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        for (AccountDO elem : accounts) {
            if (passwordEncoder.matches(form.getPassword(), elem.getPassword())) {
                account = elem;
            }
        }
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        return getAccountInfo(account);
    }

    @Override
    public AccountInfoDTO getUserAccountInfoByMachine(LoginMachineForm form) {

        String idNumber = form.getIdNumber();
        UserInfoDO userInfo = userInfoDao.findByCertNoAndCertTypeAndDeletedFalse(idNumber, CertType.ID.getValue());
        if (ObjectUtils.isEmpty(userInfo)) {
            userInfo = new UserInfoDO();
            userInfo.setCertNo(form.getIdNumber());
            userInfo.setCertType(CertType.ID.getValue());
            userInfo.setIsMachine(true);
        }
        userInfo.setUserName(form.getUserName());
        userInfo.setPhone(form.getPhone());
        userInfoDao.save(userInfo);

        OrganizationDO existOrg = organizationDao.findByTypeAndIdNumberAndDeletedFalse(OrganizationType.ALIPAY, form.getIdNumber());
        if (existOrg == null) {
            existOrg = new OrganizationDO();
            existOrg.setType(OrganizationType.ALIPAY);
            existOrg.setState(SysOnOffState.ON);
            existOrg.setOrganizationSourceId(1L);
            existOrg.setIdNumber(form.getIdNumber());
            existOrg.setAuthState(AuthState.PASS);
        }
        existOrg.setPhone(form.getPhone());
        existOrg.setUsername(form.getUserName());
        organizationDao.save(existOrg);

        AccountDO account;
        List<AccountDO> accounts = accountDao.getSingleAccount(
                userInfo.getId(), Collections.singletonList(UserType.PRIMARY), OrganizationType.ALIPAY
        );
        if (CollectionUtils.isEmpty(accounts)) {
            account = new AccountDO();
            account.setUsername(form.getPhone());
            account.setUserId(userInfo.getId());
            account.setState(SysOnOffState.ON);
            account.setType(UserType.PRIMARY);
            account.setOrigin(EndpointType.ALIPAY_APPLET);
            account.setOpenId(IdUtil.fastSimpleUUID());
            account.setOrganizationId(existOrg.getId());
            account.setPointScale(1);
            accountDao.save(account);
        } else {
            List<Long> accountIds = accounts.stream().map(AccountDO::getId).distinct().collect(Collectors.toList());
            SysOperLogDO latestLog = sysOperLogDao.findFirstByCreatorIdInAndDeletedFalseOrderByIdDesc(accountIds);
            if (ObjectUtils.isEmpty(latestLog)) {
                accounts.sort(Comparator.comparing(AccountDO::getId).reversed());
                account = accounts.get(0);
            } else {
                account = accounts.stream().filter(f -> f.getId().equals(latestLog.getCreatorId())).collect(Collectors.toList()).get(0);
            }
        }
        Long accountId = account.getId();
        List<InviteCodeDO> inviteCodes = inviteCodeDao.getInviteCodeByOperatorIdAndTypeAndDeletedFalseOrderByIdDesc(accountId, InviteCodeType.INVITE);
        if (CollectionUtils.isEmpty(inviteCodes)) {
            AddOperatorForm addOperator = new AddOperatorForm();
            addOperator.setAccountId(account.getId());
            operatorService.add(addOperator);
        }
        return getAccountInfo(account);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccountInfoDTO getUserAccountInfoByApplet(LoginAppletForm form) {
        try {
            UserPhoneInfoDTO userPhoneInfo = wxAppletApiService.getUserPhoneInfo(new GetUserInfoParam(form.getPhoneCode()));
            UserOpenIdInfoDTO userOpenIdInfo = wxAppletApiService.getUserOpenIdInfo(new GetUserInfoParam(form.getOpenIdCode()));
            UserInfoDO userInfo = userInfoDao.findByPhoneAndDeletedFalse(userPhoneInfo.getPhoneInfo().getPhoneNumber());
            if (ObjectUtils.isEmpty(userInfo)) {
                userInfo = new UserInfoDO();
                userInfo.setUserName(form.getUserName());
                userInfo.setPhone(userPhoneInfo.getPhoneInfo().getPhoneNumber());
            }
            userInfo.setOpenId(userOpenIdInfo.getOpenId());
            userInfo.setUnionId(userOpenIdInfo.getUnionId());
            userInfoDao.save(userInfo);

            AccountDO account;
            List<AccountDO> accounts = accountDao.findByUserIdAndTypeInAndDeletedFalse(userInfo.getId(), Arrays.asList(UserType.PRIMARY, UserType.SUB, UserType.NONE));
            Map<Long, OrganizationDO> organizationMap = new HashMap<>();
            List<Long> organizationIds = accounts.stream().map(AccountDO::getOrganizationId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
            if (!organizationIds.isEmpty()) {
                organizationMap = organizationDao.findByIdInAndDeletedFalse(organizationIds)
                        .stream().filter(d -> !OrganizationType.ALIPAY.equals(d.getType()))
                        .collect(Collectors.toMap(OrganizationDO::getId, d -> d));
            }
            Map<Long, OrganizationDO> finalOrganizationMap = organizationMap;
            accounts = accounts.stream().filter(s -> s.getOrganizationId() == null || finalOrganizationMap.containsKey(s.getOrganizationId())).collect(Collectors.toList());

            if (CollectionUtils.isEmpty(accounts)) {
                account = new AccountDO();
                account.setUsername(form.getUserName());
                account.setUserId(userInfo.getId());
                account.setState(SysOnOffState.ON);
                account.setType(UserType.NONE);
                account.setOrigin(EndpointType.APPLET);
                account.setOpenId(IdUtil.fastSimpleUUID());
                account.setMembershipRankId(1L);
                accountDao.save(account);
            } else {
                List<Long> accountIds = accounts.stream().map(AccountDO::getId).distinct().collect(Collectors.toList());
                SysOperLogDO latestLog = sysOperLogDao.findFirstByCreatorIdInAndDeletedFalseOrderByIdDesc(accountIds);
                if (ObjectUtils.isEmpty(latestLog)) {
                    accounts.sort(Comparator.comparing(AccountDO::getId).reversed());
                    account = accounts.get(0);
                } else {
                    account = accounts.stream().filter(f -> f.getId().equals(latestLog.getCreatorId())).collect(Collectors.toList()).get(0);
                }
            }
            if (UserType.NONE.equals(account.getType()) && account.getOrigin() == null) {
                account.setOrigin(EndpointType.APPLET);
                account.setMembershipRankId(1L);
                accountDao.save(account);
            }
            Long accountId = account.getId();
            List<InviteCodeDO> inviteCodes = inviteCodeDao.getInviteCodeByOperatorIdAndTypeAndDeletedFalseOrderByIdDesc(accountId, InviteCodeType.INVITE);
            if (CollectionUtils.isEmpty(inviteCodes)) {
                AddOperatorForm addOperator = new AddOperatorForm();
                addOperator.setAccountId(account.getId());
                operatorService.add(addOperator);
            }
            return getAccountInfo(account);
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException(51000, "小程序登陆异常");
        }
    }

    @Override
    public AccountInfoDTO getAccountInfoByAccountId(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        return getAccountInfo(account);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccountInfoDTO getUserAccountInfoByAliApplet(LoginAliAppletForm form) {
        try {
            String phone = aliAppletService.getUserPhoneInfo(form.getParam(), form.getSign());
            AssertUtil.assertTrue(StringUtils.isNotBlank(phone), SysCode.ALI_APPLET_API_PHONE_ERROR);
            UserInfoDO userInfo = userInfoDao.findByPhoneAndDeletedFalse(phone);
            if (ObjectUtils.isEmpty(userInfo)) {
                userInfo = new UserInfoDO();
                userInfo.setUserName(userInfo.getUserName());
                userInfo.setPhone(phone);
            }
            userInfoDao.save(userInfo);

            AccountDO account;
            List<AccountDO> accounts = accountDao.getSingleAccount(
                    userInfo.getId(), Arrays.asList(UserType.PRIMARY, UserType.SUB, UserType.NONE), OrganizationType.SINGLE
            );
            if (CollectionUtils.isEmpty(accounts)) {
                account = new AccountDO();
                account.setUsername(phone);
                account.setUserId(userInfo.getId());
                account.setState(SysOnOffState.ON);
                account.setType(UserType.NONE);
                account.setOpenId(IdUtil.fastSimpleUUID());
                account.setOrigin(EndpointType.ALIPAY_APPLET);
                account.setMembershipRankId(1L);
                accountDao.save(account);
                //新增用户时，如果存在邀请码，需要跟邀请码进行绑定。
                if (StringUtils.isNotBlank(form.getInviteCode())) {
                    Long accountId = account.getId();
                    InviteCodeForm inviteCodeForm = new InviteCodeForm();
                    inviteCodeForm.setCode(form.getInviteCode());
                    inviteCodeService.bindInvite(accountId, inviteCodeForm);
                }
//                //新增用户，默认新增为运营商
//                AddOperatorForm addOperatorForm = new AddOperatorForm();
//                addOperatorForm.setAccountId(account.getId());
//                operatorService.add(addOperatorForm);

            } else {
                List<Long> accountIds = accounts.stream().map(AccountDO::getId).distinct().collect(Collectors.toList());
                SysOperLogDO latestLog = sysOperLogDao.findFirstByCreatorIdInAndDeletedFalseOrderByIdDesc(accountIds);
                if (ObjectUtils.isEmpty(latestLog)) {
                    accounts.sort(Comparator.comparing(AccountDO::getId).reversed());
                    account = accounts.get(0);
                } else {
                    account = accounts.stream().filter(f -> f.getId().equals(latestLog.getCreatorId())).collect(Collectors.toList()).get(0);
                }
            }
            if (UserType.NONE.equals(account.getType()) && account.getOrigin() == null) {
                account.setOrigin(EndpointType.ALIPAY_APPLET);
                account.setMembershipRankId(1L);
                accountDao.save(account);
            }
            Long accountId = account.getId();
            List<InviteCodeDO> inviteCodes = inviteCodeDao.getInviteCodeByOperatorIdAndTypeAndDeletedFalseOrderByIdDesc(accountId, InviteCodeType.INVITE);
            if (CollectionUtils.isEmpty(inviteCodes)) {
                AddOperatorForm addOperator = new AddOperatorForm();
                addOperator.setAccountId(account.getId());
                operatorService.add(addOperator);
            }

            return getAccountInfo(account);
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            throw new ServiceException(51000, "小程序登陆异常");
        }
    }

    @Override
    public AccountInfoDTO getUserAccountInfoByAliAppletWithoutVerify(LoginAliAppletWithoutVerifyForm form) {
        try {
            AlipayUserInfoShareResponseDTO aliUserInfo = aliAppletService.getUserInfo(form.getParam());
            AssertUtil.assertTrue((aliUserInfo != null), SysCode.ALI_APPLET_API_USER_INFO_ERROR);

            UserInfoDO userInfo = userInfoDao.findByPhoneAndDeletedFalse(aliUserInfo.getMobile());
            if (ObjectUtils.isEmpty(userInfo)) {
                userInfo = new UserInfoDO();
                userInfo.setPhone(aliUserInfo.getMobile());
            }
            userInfo.setCertNo(aliUserInfo.getCert_no());
            userInfo.setCertType(aliUserInfo.getCert_type());
            userInfo.setUserName(aliUserInfo.getUser_name());
            userInfo.setAliUserId(aliUserInfo.getUser_id());
            userInfoDao.save(userInfo);

            AccountDO account;
            List<AccountDO> accounts = accountDao.getSingleAccount(
                    userInfo.getId(), Arrays.asList(UserType.PRIMARY, UserType.SUB, UserType.NONE), OrganizationType.ALIPAY
            );
            OrganizationDO existOrg = organizationDao.findByTypeAndIdNumberAndDeletedFalse(
                    OrganizationType.ALIPAY, aliUserInfo.getCert_no()
            );
            if (existOrg == null) {
                existOrg = new OrganizationDO();
                existOrg.setOrganizationSourceId(1L);
                existOrg.setType(OrganizationType.ALIPAY);
                existOrg.setState(SysOnOffState.ON);
            }
            existOrg.setUsername(aliUserInfo.getUser_name());
            existOrg.setPhone(aliUserInfo.getMobile());
            existOrg.setIdNumber(aliUserInfo.getCert_no());
            existOrg.setAuthState(AuthState.PASS);
            organizationDao.save(existOrg);

            OrganizationType type = existOrg.getType();
            if (CollectionUtils.isEmpty(accounts)) {
                account = new AccountDO();
                account.setUsername(aliUserInfo.getUser_name());
                account.setAvatar(aliUserInfo.getAvatar());
                account.setType(UserType.PRIMARY);
                account.setUserId(userInfo.getId());
                account.setOrganizationId(existOrg.getId());
                account.setState(SysOnOffState.ON);
                account.setOpenId(IdUtil.fastSimpleUUID());
                account.setOrigin(EndpointType.ALIPAY_APPLET);
                account.setMembershipRankId(1L);
                account.setPointScale(1);
                accountDao.save(account);

                //新增用户时，如果存在邀请码，需要跟邀请码进行绑定。
                if (StringUtils.isNotBlank(form.getInviteCode())) {
                    Long accountId = account.getId();
                    InviteCodeForm inviteCodeForm = new InviteCodeForm();
                    inviteCodeForm.setCode(form.getInviteCode());
                    inviteCodeService.bindInvite(accountId, inviteCodeForm);
                }
            } else {
                List<Long> accountIds = accounts.stream().map(AccountDO::getId).distinct().collect(Collectors.toList());
                SysOperLogDO latestLog = sysOperLogDao.findFirstByCreatorIdInAndDeletedFalseOrderByIdDesc(accountIds);
                if (ObjectUtils.isEmpty(latestLog)) {
                    accounts.sort(Comparator.comparing(AccountDO::getId).reversed());
                    account = accounts.get(0);
                } else {
                    account = accounts.stream().filter(f -> f.getId().equals(latestLog.getCreatorId())).collect(Collectors.toList()).get(0);
                }
            }
            account.setUsername(aliUserInfo.getUser_name());
            account.setAvatar(aliUserInfo.getAvatar());
            if (UserType.NONE.equals(account.getType())) {
                account.setType(UserType.PRIMARY);
                account.setMembershipRankId(1L);
                account.setOrganizationId(existOrg.getId());
                account.setOrigin(EndpointType.ALIPAY_APPLET);
            }
            accountDao.save(account);
            if (!existOrg.getId().equals(account.getOrganizationId())) {
                OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(account.getOrganizationId());
                organization.setAuthState(AuthState.PASS);
                if (OrganizationType.SINGLE.equals(organization.getType())) {
                    existOrg.setUsername(aliUserInfo.getUser_name());
                    existOrg.setPhone(aliUserInfo.getMobile());
                    existOrg.setIdNumber(aliUserInfo.getCert_no());
                }
                type = organization.getType();
                organizationDao.save(existOrg);
            }

            AccountInfoDTO res = new AccountInfoDTO();
            res.setAccountId(account.getId());
            res.setUsername(account.getUsername());
            res.setOrganizationId(account.getOrganizationId());
            res.setType(type);
            Long accountId = account.getId();
            List<InviteCodeDO> inviteCodes = inviteCodeDao.getInviteCodeByOperatorIdAndTypeAndDeletedFalseOrderByIdDesc(accountId, InviteCodeType.INVITE);
            if (CollectionUtils.isEmpty(inviteCodes)) {
                AddOperatorForm addOperator = new AddOperatorForm();
                addOperator.setAccountId(account.getId());
                operatorService.add(addOperator);
            }
            return res;
        } catch (ServiceException e) {
            throw e;
        } catch (Exception e) {
            log.error("小程序登录异常", e);
            throw new ServiceException(51000, "小程序登陆异常");
        }
    }

    @Override
    public AccountInfoDTO getUserAccountInfoById(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        if (ObjectUtils.isEmpty(account)) {
            return new AccountInfoDTO();
        }
        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(account.getOrganizationId());
        if (ObjectUtils.isEmpty(organization)) {
            log.error("账号：{} 绑定组织：{} 不存在", accountId, account.getOrganizationId());
        }
        AccountInfoDTO accountInfo = new AccountInfoDTO(account.getId(), account.getUsername(), account.getOrganizationId(),
                ObjectUtils.isEmpty(organization) ? null : organization.getType(), account.getOrigin());
        accountInfo.setUserId(account.getUserId());
        accountInfo.setOrganizationId(account.getOrganizationId());
        return accountInfo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(LoginForm form) {
        List<AccountDO> accounts = accountDao.findAccountByUsernameAndTypeAndStateAndDeletedFalse(form.getUsername(), UserType.ADMIN, SysOnOffState.ON);
        AssertUtil.assertTrue(CollectionUtils.isEmpty(accounts), AccountExceptionEnum.ACCOUNT_EXIST);

        AccountDO account = new AccountDO();
        account.setUsername(form.getUsername());
        account.setPassword(passwordEncoder.encode(form.getPassword()));
        account.setState(SysOnOffState.ON);
        account.setType(UserType.ADMIN);
        account.setOrigin(EndpointType.PLATFORM);
        account.setOpenId(IdUtil.fastSimpleUUID());
        accountDao.save(account);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean modify(ModifySysUserInfoForm form) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(form.getId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);

        if (StringUtils.isNotBlank(form.getUsername())) {
            account.setUsername(form.getUsername());
        }

        if (StringUtils.isNotBlank(form.getPassword())) {
            account.setPassword(passwordEncoder.encode(form.getPassword()));
        }

        if (StringUtils.isNotBlank(form.getState())) {
            SysOnOffState state = SysOnOffState.valueOf(form.getState());
            AssertUtil.assertTrue(!ObjectUtils.isEmpty(state), AccountExceptionEnum.ACCOUNT_STATE_NOT_EXIST);
            account.setState(state);
        }

        accountDao.save(account);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(Long accountId, List<Long> id) {
        accountDao.deleteByIdIn(id, accountId, new Timestamp(System.currentTimeMillis()));
        return true;
    }

    @Override
    public PageVO<SysUserInfoVO> list(GetSysUserInfoForm form, Pageable pageable) {
        Page<AccountDO> accounts = accountDao.getAccountByUsernameLikeAndTypeAndDeletedFalse(form.getUsername(), UserType.ADMIN, pageable);

        return new PageVO<>(accounts.getTotalElements(), accounts.getContent().stream().map(elem ->
                new SysUserInfoVO(elem.getId(), elem.getUsername(), elem.getState(), elem.getCreateTime())
        ).collect(Collectors.toList()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean registerOrg(Long accountId, RegisterForm form) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        UserInfoDO userInfo = userInfoDao.findByIdAndDeletedFalse(account.getUserId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(userInfo), UserExceptionEnum.USER_NOT_EXIST);

        boolean verifyName;
        OrganizationDO organization = new OrganizationDO();
        if (OrganizationType.ENTER.equals(form.getType())) {
            verifyName = organizationDao.countDistinctByTypeAndEnterNameAndDeletedFalse(OrganizationType.ENTER, form.getEnterName()) == 0;
            if (!verifyName) {
                organization = organizationDao.findInfoByTypeAndEnterNameAndOrganizationSourceIdAndDeletedFalse(OrganizationType.ENTER, form.getEnterName(),
                        4L);
                if (organization == null) {
                    AssertUtil.assertTrue(verifyName, OrganizationExceptionEnum.ORGANIZATION_EXIST);
                }
                organization.setLicense(form.getLicense());
                organization.setIdNumber(form.getIdNumber());
                organization.setPhone(form.getPhone());
                organization.setEnterAddr(form.getEnterAddr());
                organization.setTaxpayerCode(form.getTaxpayerCode());
                organization.setAuthFile(form.getAuthFile());
                organization.setPerAuthFile(form.getPerAuthFile());
                organization.setEntAuthFile(form.getEntAuthFile());
            } else {
                organization.setOrganizationSourceId(1L);
                //创建企业
                form.setId(null);
                BeanUtils.copyProperties(form, organization);
                organization.setState(SysOnOffState.ON);
                organization.setAuthState(StringUtils.isNotBlank(form.getAuthFile()) ? AuthState.PASS : AuthState.TO_VERIFY);
                if (StringUtils.isBlank(form.getPhone())) {
                    organization.setPhone(userInfo.getPhone());
                }
            }
        } else {
            verifyName = organizationDao.countDistinctByTypeAndIdNumberAndDeletedFalse(OrganizationType.SINGLE, form.getIdNumber()) == 0;
            AssertUtil.assertTrue(verifyName, OrganizationExceptionEnum.ORGANIZATION_EXIST);
            organization.setOrganizationSourceId(1L);
            //创建企业
            form.setId(null);
            BeanUtils.copyProperties(form, organization);
            organization.setState(SysOnOffState.ON);
            if (form.getTwoFactorAuthState() == null) {//兼容老系统
                organization.setAuthState(
                        StringUtils.isNotBlank(form.getAuthFile()) ? AuthState.PASS : AuthState.TO_VERIFY);
            } else {
                organization.setAuthState(
                        form.getTwoFactorAuthState() ? AuthState.PASS : AuthState.TO_VERIFY);
                //更新用户身份证个人
                if (form.getTwoFactorAuthState()) {
                    userInfo.setCertNo(form.getIdNumber());
                    userInfo.setUserName(form.getUsername());
                    userInfoDao.save(userInfo);
                }
            }
            if (StringUtils.isBlank(form.getPhone())) {
                organization.setPhone(userInfo.getPhone());
            }
        }
        organizationDao.save(organization);
        if (ObjectUtils.isEmpty(account.getOrganizationId())) {
            if (OrganizationType.SINGLE.equals(form.getType())) {
                account.setUsername(form.getUsername());
            }
            account.setType(UserType.PRIMARY);
            account.setOrganizationId(organization.getId());
            accountDao.save(account);
        } else {
            boolean verifyPhone = accountDao.countDistinctByUserIdAndOrganizationIdAndDeletedFalse(userInfo.getId(), organization.getId()) == 0;
            AssertUtil.assertTrue(verifyPhone, AccountExceptionEnum.ACCOUNT_EXIST);
            // 创建新账号
            account = new AccountDO();
            if (OrganizationType.SINGLE.equals(form.getType())) {
                account.setUsername(form.getUsername());
            } else {
                account.setUsername(userInfo.getPhone());
            }
            account.setPointScale(1);
            account.setOrigin(EndpointType.APPLET);
            account.setMembershipRankId(1L);
            account.setType(UserType.PRIMARY);
            account.setUserId(userInfo.getId());
            account.setOrganizationId(organization.getId());
            account.setState(SysOnOffState.ON);
            account.setOpenId(IdUtil.fastSimpleUUID());
            accountDao.save(account);
        }

        // 查询邀请码
        String inviteCode = inviteRecordDao.findRecordByCreatorIdAndTypeAndDeletedFalse(accountId, InviteCodeType.INVITE)
                .stream().max(Comparator.comparing(InviteRecordDO::getId)).orElse(new InviteRecordDO()).getInviteCode();
        form.setInviteCode(inviteCode);
        // 绑定邀请码
        inviteCodeService.bindInviteRecord(form.getInviteCode(), organization.getId(), accountId);

        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String registerBind(RegisterBindForm form) {
        UserInfoDO userInfo = userInfoDao.findByPhoneAndDeletedFalse(form.getPhone());
        if (ObjectUtils.isEmpty(userInfo)) {
            userInfo = new UserInfoDO();
            userInfo.setUserName(form.getUsername());
            userInfo.setPhone(form.getPhone());
            userInfo.setUserName(userInfo.getPhone());
            userInfoDao.save(userInfo);
        }

        //生成跳转微信短连接
        Map<String, Object> accessTokenMap = new HashMap<>(16);
        accessTokenMap.put("grant_type", "client_credential");
        accessTokenMap.put("appid", APPID);
        accessTokenMap.put("secret", SECRET);
        String accessToken = HttpUtil.get(ACCESS_TOKEN, accessTokenMap);
        log.info("微信返回的accessToken: {}", accessToken);
        WxAccessTokenDTO wxAccessToken = JSON.parseObject(accessToken, WxAccessTokenDTO.class);
        UrlSchemeQueryDTO urlSchemeQuery = new UrlSchemeQueryDTO();
        urlSchemeQuery.setExpireType(1);
        urlSchemeQuery.setExpireInterval(1);
        JumpWxaDTO jumpWxa = new JumpWxaDTO();
        jumpWxa.setPath("/pages/view/menuBar");
        urlSchemeQuery.setJumpWxa(jumpWxa);
        String urlSchemeStr = HttpUtil.post(URL_SCHEME + "?access_token=" + wxAccessToken.getAccessToken(), JSON.toJSONString(urlSchemeQuery));
        UrlSchemeDTO urlScheme = JSON.parseObject(urlSchemeStr, UrlSchemeDTO.class);
        String openLink = null;
        if (StringUtils.isNotBlank(urlScheme.getOpenLink())) {
            openLink = urlScheme.getOpenLink();
        } else if (StringUtils.isNotBlank(urlScheme.getErrMsg())) {
            log.error("微信生成短连接返回的错误信息: {}", urlScheme.getErrMsg());
            openLink = "";
        }

        List<AccountDO> accounts = accountDao.findByUserIdAndTypeInAndDeletedFalse(userInfo.getId(), Arrays.asList(UserType.PRIMARY,
                UserType.SUB, UserType.NONE));

        if (!CollectionUtils.isEmpty(accounts)) {
            throw new RuntimeException("该手机号已注册");
        }

        AccountDO account = new AccountDO();
        account.setUsername(form.getUsername());
        account.setUserId(userInfo.getId());
        account.setState(SysOnOffState.ON);
        account.setType(UserType.NONE);
        account.setEnterName(form.getEnterName());
        account.setOpenId(IdUtil.fastSimpleUUID());
        accountDao.save(account);

        if (StringUtils.isBlank(form.getSign())) {
            log.info("用户: {}, 手机号: {} 绑定的签名为空", form.getUsername(), form.getPhone());
            return "";
        }

        InviteCodeDO codeInfo = inviteCodeDao.findCodeByInviteCodeAndDeletedFalse(form.getSign());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(codeInfo), InviteCodeExceptionEnum.INVITE_CODE_NOT_EXIST);
        AssertUtil.assertTrue(ObjectUtils.isEmpty(codeInfo.getExpiresTime()) || codeInfo.getExpiresTime().compareTo(new Timestamp(System.currentTimeMillis())) >= 0, InviteCodeExceptionEnum.INVITE_CODE_EXPIRED);

        if (!ObjectUtils.isEmpty(codeInfo.getUseTimes())) {
            List<InviteRecordDO> recordList = inviteRecordDao.findRecordByInviteCodeAndDeletedFalse(form.getSign());
            AssertUtil.assertTrue(CollectionUtils.isEmpty(recordList) || recordList.size() < codeInfo.getUseTimes(), InviteCodeExceptionEnum.INVITE_CODE_INVALID);
        }

        InviteRecordDO data = new InviteRecordDO();
        data.setType(codeInfo.getType());
        data.setInviteCode(form.getSign());
        data.setInviteTime(new Timestamp(System.currentTimeMillis()));
        data.setCreatorId(account.getId());
        inviteRecordDao.save(data);

        return openLink;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public TokenVO registerBindShare(RegisterBindShareForm form) {

//        Long accountId = form.getAccountId();
//        AccountDO existAccount = accountDao.findByIdAndDeletedFalse(form.getAccountId());
//        AssertUtil.assertTrue(!ObjectUtils.isEmpty(existAccount), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
//        Long organizationId = existAccount.getOrganizationId();
//        UserInfoDO existUser = userInfoDao.findByIdAndDeletedFalse(existAccount.getUserId());
//        AssertUtil.assertTrue(!ObjectUtils.isEmpty(existUser), UserExceptionEnum.USER_NOT_EXIST);
//        existUser.setCertNo(form.getIdNumber());
//        userInfoDao.save(existUser);
        //创建企业
//        OrganizationDO organization = new OrganizationDO();
//        organization.setOrganizationSourceId(1L);
//        organization.setState(SysOnOffState.ON);
//        organization.setAuthState(AuthState.PASS);
//        organization.setUsername(form.getUsername());
//        organization.setIdNumber(form.getIdNumber());
//        organization.setPhone(existUser.getPhone());
//        if ("SINGLE".equals(form.getRegisterType())) {
//            organization.setEnterName(form.getUsername());
//            organization.setType(OrganizationType.SINGLE);
//        } else if ("ENTER".equals(form.getRegisterType())) {
//            organization.setEnterName(form.getOrgName());
//            organization.setType(OrganizationType.ENTER);
//        }
        //      organizationDao.save(organization);
//        if (organizationId == null) {
//            //创建企业
//            OrganizationDO organization = new OrganizationDO();
//            organization.setOrganizationSourceId(1L);
//            organization.setState(SysOnOffState.ON);
//            organization.setAuthState(AuthState.PASS);
//            organization.setUsername(form.getUsername());
//            organization.setIdNumber(form.getIdNumber());
//            organization.setPhone(existUser.getPhone());
//            if ("SINGLE".equals(form.getRegisterType())) {
//                organization.setEnterName(form.getUsername());
//                organization.setType(OrganizationType.SINGLE);
//            } else if ("ENTER".equals(form.getRegisterType())) {
//                organization.setEnterName(form.getOrgName());
//                organization.setType(OrganizationType.ENTER);
//            }
//            organizationDao.save(organization);
//            organizationId = organization.getId();
//
//            existAccount.setUsername(form.getUsername());
//            existAccount.setType(UserType.PRIMARY);
//            existAccount.setOrigin(EndpointType.APPLET);
//            existAccount.setEnterName("SINGLE".equals(form.getRegisterType()) ? form.getUsername() : form.getOrgName());
//            existAccount.setOrganizationId(organization.getId());
//            existAccount.setPointScale(1);
//            existAccount.setMembershipRankId(1L);
//            accountDao.save(existAccount);
//        }
//        else {
//            //创建账号
//            AccountDO newAccount = new AccountDO();
//            newAccount.setUsername(form.getUsername());
//            newAccount.setUserId(existAccount.getUserId());
//            newAccount.setState(SysOnOffState.ON);
//            newAccount.setType(UserType.PRIMARY);
//            newAccount.setOrigin(EndpointType.APPLET);
//            newAccount.setEnterName("SINGLE".equals(form.getRegisterType()) ? form.getUsername() : form.getOrgName());
//            newAccount.setOpenId(IdUtil.fastSimpleUUID());
//            newAccount.setOrganizationId(organization.getId());
//            newAccount.setPointScale(1);
//            newAccount.setMembershipRankId(1L);
//            accountDao.save(newAccount);
//            accountId = newAccount.getId();
//        }
//        InviteRecordDO data = new InviteRecordDO();
//        data.setType(InviteCodeType.INVITE);
//        data.setInviteCode(form.getSign());
//        data.setOrganizationId(existAccount.getOrganizationId());
//        data.setInviteTime(new Timestamp(System.currentTimeMillis()));
//        data.setCreatorId(accountId);
//        inviteRecordDao.save(data);
//
//        AccountInfoDTO accountInfo = new AccountInfoDTO();
//        accountInfo.setAccountId(accountId);
//        accountInfo.setUsername(form.getUsername());
//        accountInfo.setOrganizationId(organizationId);
//        accountInfo.setType(organizationDao.findByIdAndDeletedFalse(organizationId).getType());
//        accountInfo.setEndpoint(EndpointType.APPLET);
//        log.info("注册绑定邀请码成功, 账号: {}, 邀请码: {}", accountInfo, form.getSign());
//        TokenVO token = JwtUtil.generateTokens(accountInfo);
//        String key = EndpointType.APPLET.name() + "_" + accountId.toString();
//        JwtUtil.revokeUserToken(key);
//        JwtUtil.saveToken(key, token.getAccessToken());

        UserInfoDO userInfo = userInfoDao.findByPhoneAndDeletedFalse(form.getPhone());
        if (ObjectUtils.isEmpty(userInfo)) {
            userInfo = new UserInfoDO();
            userInfo.setPhone(form.getPhone());
            userInfo.setUserName(form.getUsername());
            userInfo.setCertNo(form.getIdNumber());
            userInfo.setCertType("0");
            userInfoDao.save(userInfo);
        }
        //注册企业
        OrganizationDO organization = new OrganizationDO();
        organization.setOrganizationSourceId(1L);
        organization.setState(SysOnOffState.ON);
        organization.setAuthState(AuthState.PASS);
        organization.setUsername(form.getUsername());
        organization.setIdNumber(form.getIdNumber());
        organization.setPhone(form.getPhone());
        if ("SINGLE".equals(form.getRegisterType())) {
            organization.setEnterName(form.getUsername());
            organization.setType(OrganizationType.SINGLE);
        } else if ("ENTER".equals(form.getRegisterType())) {
            organization.setEnterName(form.getOrgName());
            organization.setType(OrganizationType.ENTER);
        }
        organizationDao.save(organization);
        Long organizationId = organization.getId();
        // 注册用户
        AccountDO newAccount = new AccountDO();
        newAccount.setUsername(form.getUsername());
        newAccount.setUserId(userInfo.getId());
        newAccount.setState(SysOnOffState.ON);
        newAccount.setType(UserType.PRIMARY);
        newAccount.setOrigin(EndpointType.APPLET);
        newAccount.setEnterName("SINGLE".equals(form.getRegisterType()) ? form.getUsername() : form.getOrgName());
        newAccount.setOpenId(IdUtil.fastSimpleUUID());
        newAccount.setOrganizationId(organization.getId());
        newAccount.setPointScale(1);
        newAccount.setMembershipRankId(1L);
        accountDao.save(newAccount);
        Long accountId = newAccount.getId();
        //绑定分享关系
        InviteRecordDO data = new InviteRecordDO();
        data.setType(InviteCodeType.INVITE);
        data.setInviteCode(form.getSign());
        data.setOrganizationId(organizationId);
        data.setInviteTime(new Timestamp(System.currentTimeMillis()));
        data.setCreatorId(accountId);
        inviteRecordDao.save(data);
        //创建登录token
        AccountInfoDTO accountInfo = new AccountInfoDTO();
        accountInfo.setAccountId(accountId);
        accountInfo.setUsername(form.getUsername());
        accountInfo.setOrganizationId(organizationId);
        accountInfo.setType(organization.getType());
        accountInfo.setEndpoint(EndpointType.APPLET);
        log.info("注册绑定邀请码成功, 账号: {}, 邀请码: {}", accountInfo, form.getSign());
        TokenVO token = JwtUtil.generateTokens(accountInfo);
        String key = EndpointType.APPLET.name() + "_" + accountId.toString();
        JwtUtil.revokeUserToken(key);
        JwtUtil.saveToken(key, token.getAccessToken());
        return token;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean modifyOrg(Long accountId, RegisterForm form) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(form.getId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(organization), OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);
        BeanUtils.copyProperties(form, organization);
        if (OrganizationType.ENTER.equals(organization.getType()) && StringUtils.isNotBlank(form.getAuthFile()) && !AuthState.PASS.equals(organization.getAuthState())) {
            organization.setAuthState(AuthState.PASS);
        }
        organizationDao.save(organization);
        if (OrganizationType.SINGLE.equals(organization.getType())) {
            account.setUsername(form.getUsername());
            accountDao.save(account);
        }
        // 为来源企业创建新账号
        if (account.getOrganizationId() != null && !account.getOrganizationId().equals(form.getId())) {
            AccountDO newAccount = new AccountDO();
            BeanUtils.copyProperties(account, newAccount);
            newAccount.setId(null);
            newAccount.setCreatorId(accountId);
            newAccount.setCreateTime(new Timestamp(System.currentTimeMillis()));
            newAccount.setModifierId(null);
            newAccount.setModifyTime(null);
            newAccount.setOrganizationId(form.getId());
            accountDao.save(newAccount);
        }
        if (account.getOrganizationId() == null) {
            account.setOrganizationId(form.getId());
            accountDao.save(account);
        }
        // 绑定邀请码
        // inviteCodeService.bindInviteRecord(form.getInviteCode(), organization.getId(), accountId);

        return true;
    }

    @Override
    public RegisterForm getOrg(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(account.getOrganizationId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(organization), OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);
        Long organizationSourceId = organization.getOrganizationSourceId();
        OrganizationSourceDO organizationSource = organizationSourceDao.findInfoByIdAndDeletedFalse(organizationSourceId);
        RegisterForm res = new RegisterForm();
        BeanUtils.copyProperties(organization, res);
        res.setNeedPerAuthFile(organizationSource.getPerAuth());
        res.setNeedEntAuthFile(organizationSource.getEntAuth());
        List<Long> userIds = accountDao.findAccountByOrganizationIdAndDeletedFalse(organization.getId()).stream().
                map(AccountDO::getId).collect(Collectors.toList());
        Timestamp now = new Timestamp(System.currentTimeMillis());
        List<QueryRecordDO> queryRecordInfos = queryRecordDao.findInfosByCreatorIdInAndIsFileAndExpiresTimeLessThanAndDeletedFalse(userIds, true, now);
        if (!CollectionUtils.isEmpty(queryRecordInfos)) {
            List<Long> queryIds = queryRecordInfos.stream().map(QueryRecordDO::getId).collect(Collectors.toList());
            List<FileRelativeType> relativeTypes = Arrays.asList(FileRelativeType.ENT_AUTH, FileRelativeType.PER_AUTH);
            List<SysFileDO> files = sysFileDao.findInfosByRelativeIdInAndRelativeTypeInAndDeletedFalse(queryIds, relativeTypes).
                    stream().filter(s -> s.getUrl() != null).collect(Collectors.toList());
            if (queryIds.size() != files.size()) {
                res.setAccountState(AccountState.FROZEN);
            }
        }
        return res;
    }

    @Override
    public UserInfoVO getUserInfo(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        UserInfoDO userInfo = userInfoDao.findByIdAndDeletedFalse(account.getUserId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(userInfo), UserExceptionEnum.USER_NOT_EXIST);

        UserInfoVO res = new UserInfoVO();
        res.setAccountId(accountId);
        res.setOpenId(userInfo.getOpenId());
        res.setUsername(account.getUsername());
        res.setPhone(userInfo.getPhone());
        res.setCertNo(userInfo.getCertNo() == null ? "" : userInfo.getCertNo());
        if (!ObjectUtils.isEmpty(account.getOrganizationId())) {
            OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(account.getOrganizationId());
            AssertUtil.assertTrue(!ObjectUtils.isEmpty(organization), OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);
            res.setOrgId(account.getOrganizationId());
            res.setOrgName(organization.getEnterName());
            res.setOrgType(organization.getType());
            res.setUserType(account.getType());
            if (organization != null && StringUtils.isNotBlank(organization.getIdNumber())) {
                res.setCertNo(organization.getIdNumber());
            }
            res.setUserAuthState(UserAuthState.getState(organization.getAuthState()));

            if (OrganizationType.ENTER.equals(organization.getType())) {
                String url = "http://47.100.124.208:8910/#/?sign=" + account.getOpenId() + "&name=" + organization.getEnterName();
                res.setQueryCode(getQRCode(url));
            }
        } else {
            res.setUserAuthState(UserAuthState.NOT_REGISTER);
        }

        // 查询用户 运营商/服务商 码
        List<InviteCodeDO> codes = inviteCodeDao.findByOperatorIdAndTypeInAndDeletedFalse(account.getId(), Arrays.asList(InviteCodeType.INVITE, InviteCodeType.DISTRIBUTE));
        if (!CollectionUtils.isEmpty(codes)) {
            InviteCodeDO userCode = codes.stream().max(Comparator.comparing(InviteCodeDO::getId)).orElse(null);
            if (userCode != null) {
                res.setIsOperator(true);
                // String url = "https://admin.njxnet.com:8901/api/profile/file?inviteCode=" + userCode.getInviteCode() + "&username=" + account.getUsername();
                String url = "https://admin.njxnet.com/vipPay/#/?inviteCode=" + userCode.getInviteCode() + "&username=" + account.getUsername();
                res.setOperatorCode(getQRCode(url));
                res.setIsShowInfo(userCode.getVipUseTimes() != null && userCode.getVipUseTimes() > 0);
            }
            log.info("账号id为：{}", accountId);
            log.info("查询到的邀请码为：{}", userCode.getInviteCode());
            res.setInviteCode(userCode.getInviteCode());
            res.setIsDistributor(codes.stream().anyMatch(d -> InviteCodeType.DISTRIBUTE.equals(d.getType())));
            res.setIsSupplier(codes.stream().anyMatch(d -> InviteCodeType.INVITE.equals(d.getType())));
        }

//        String inviteCode = inviteRecordDao.findRecordByCreatorIdAndTypeAndDeletedFalse(accountId, InviteCodeType.INVITE)
//                .stream().max(Comparator.comparing(InviteRecordDO::getId)).orElse(new InviteRecordDO()).getInviteCode();
//        if (StringUtils.isNotBlank(inviteCode)) {
//            res.setInviteCode(inviteCode);
//        }
        //查询来源企业用户需上传查询授权文件数量
        List<QueryRecordDO> perQueryRecordInfos = queryRecordDao.findInfoByCreatorIdInAndTypeAndIsFileAndIsUploadAndDeletedFalse(Collections.singletonList(accountId),
                SearchType.PERSON, true, 0);
        List<QueryRecordDO> entQueryRecordInfos = queryRecordDao.findInfoByCreatorIdInAndTypeAndIsFileAndIsUploadAndDeletedFalse(Collections.singletonList(accountId),
                SearchType.ENTERPRISE, true, 0);
        res.setNeedUploadNum(perQueryRecordInfos.size() + entQueryRecordInfos.size());
        return res;
    }

    public static String getQRCode(String url) {
        String base64Url = "data:image/png;base64,";
        BufferedImage bufferedImage = QrCodeUtil.generate(url, 300, 300);
        byte[] bytes = ImgUtil.toBytes(bufferedImage, "png");
        return base64Url + Base64Encoder.encode(bytes);
    }

    @Override
    public AccountInfoDTO exchange(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        return getAccountInfo(account);
    }

    @Override
    public List<AccountInfoVO> listOrg(Long accountId) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        UserInfoDO userInfo = userInfoDao.findByIdAndDeletedFalse(account.getUserId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(userInfo), UserExceptionEnum.USER_NOT_EXIST);
        List<AccountDO> accounts = accountDao.findByUserIdAndTypeInAndDeletedFalse(userInfo.getId(), Arrays.asList(UserType.PRIMARY, UserType.SUB));
        Map<Long, OrganizationDO> organizationMap = new HashMap<>();
        List<Long> organizationIds = accounts.stream().map(AccountDO::getOrganizationId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        if (!organizationIds.isEmpty()) {
            organizationMap = organizationDao.findByIdInAndDeletedFalse(organizationIds)
                    .stream().filter(d -> !OrganizationType.ALIPAY.equals(d.getType()))
                    .collect(Collectors.toMap(OrganizationDO::getId, d -> d));
        }
        Map<Long, OrganizationDO> finalOrganizationMap = organizationMap;
        return accounts.stream().filter(f -> f.getOrganizationId() == null || finalOrganizationMap.containsKey(f.getOrganizationId())).map(data -> {
            AccountInfoVO res = new AccountInfoVO();
            res.setAccountId(data.getId());
            log.info("切换账号所用的accountId:{}", accountId);
            res.setUsername(data.getUsername());
            res.setPhone(userInfo.getPhone());
            res.setIsCurrent(data.getId().equals(accountId));
//            if (finalOrganizationMap.containsKey(data.getOrganizationId())) {
            OrganizationDO organization = finalOrganizationMap.get(data.getOrganizationId());
            res.setType(organization.getType());
            res.setEnterName(organization.getEnterName());
//            }
            return res;
        }).collect(Collectors.toList());
    }

    @Override
    public String verifyAccount(Long userId) {
        AccountDO account = this.accountDao.findByIdAndDeletedFalse(userId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), (CodeEnum)AccountExceptionEnum.ACCOUNT_NOT_EXIST);

        OrganizationDO organization = this.organizationDao.findByIdAndDeletedFalse(account.getOrganizationId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(organization), (CodeEnum)OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);

//        if (!AuthState.PASS.equals(organization.getAuthState()))
//            return AccountState.AUTH_NOT_PASS.name();

        //小程序可查询次数
        Integer searchTimesApplet = agentService.getSearchTimesApplet(userId);
        //H5可查询次数
        Integer searchTimesH5 = agentService.getSearchTimesH5(userId);

        Integer usedTimesApplet = agentService.getUsedTimesApplet(userId);
        Integer usedTimesH5 = agentService.getUsedTimesH5(userId);

        if (searchTimesApplet +  searchTimesH5 ==  0)
            return AccountState.NOT_BUY.name();
        if (searchTimesApplet + searchTimesH5 <= usedTimesApplet + usedTimesH5)
            return AccountState.USED_UP.name();
        return AccountState.NORMAL.name();
    }


    /**
     * 判断是否是试用
     */
    private int getAccountTrialNum(Long accountId) {
        int res = 0;
        List<InviteRecordDO> inviteRecords = inviteRecordDao.findRecordByCreatorIdAndTypeAndDeletedFalse(accountId, InviteCodeType.INVITE);
        if (!CollectionUtils.isEmpty(inviteRecords)) {
            InviteRecordDO validRecord = inviteRecords.stream().max(Comparator.comparing(InviteRecordDO::getCreateTime)).orElse(new InviteRecordDO());
            if (StringUtils.isNotBlank(validRecord.getInviteCode())) {
                InviteCodeDO codeInfo = inviteCodeDao.findCodeByInviteCodeAndTypeAndDeletedFalse(validRecord.getInviteCode(), InviteCodeType.INVITE);
                if (codeInfo != null && codeInfo.getVipUseTimes() != null && codeInfo.getVipUseTimes() > 0) {
                    res = codeInfo.getVipUseTimes();
                }
            }
        }
        return res;
    }

    @Override
    public List<AccountInfoVO> searchAccount(SearchAccountForm form) {
        List<IAccountDao.AccountUserInfo> page = accountDao.searchAccountInfo(form.getPhone(), Arrays.asList(UserType.PRIMARY, UserType.SUB, UserType.NONE));
        if (CollectionUtils.isEmpty(page)) {
            return Collections.emptyList();
        }

        Map<Long, OrganizationDO> organizationMap = new HashMap<>();
        List<Long> organizationIds = page.stream().map(IAccountDao.AccountUserInfo::getOrganizationId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        if (!organizationIds.isEmpty()) {
            organizationMap = organizationDao.findByIdInAndDeletedFalse(organizationIds).stream().collect(Collectors.toMap(OrganizationDO::getId, d -> d));
        }

        Map<Long, OrganizationDO> finalOrganizationMap = organizationMap;
        List<Long> operatorIds = inviteCodeDao.getOperatorByTypeAndDeletedFalse(InviteCodeType.INVITE).stream().map(InviteCodeDO::getOperatorId).distinct().collect(Collectors.toList());

        return page.stream().filter(data -> !operatorIds.contains(data.getAccountId())).map(data -> {
            AccountInfoVO res = new AccountInfoVO();
            res.setAccountId(data.getAccountId());
            res.setUsername(data.getUsername());
            res.setPhone(data.getPhone());
            if (finalOrganizationMap.containsKey(data.getOrganizationId())) {
                OrganizationDO organization = finalOrganizationMap.get(data.getOrganizationId());
                res.setType(organization.getType());
                res.setEnterName(organization.getEnterName());
            }
            return res;
        }).collect(Collectors.toList());
    }

    @Override
    public List<AccountInfoVO> searchOrgAccount(SearchAccountForm form) {
        List<IAccountDao.AccountUserInfo> page = accountDao.searchAccountInfo(form.getPhone(), Arrays.asList(UserType.PRIMARY, UserType.SUB));
        if (CollectionUtils.isEmpty(page)) {
            return Collections.emptyList();
        }

        Map<Long, OrganizationDO> organizationMap = new HashMap<>();
        List<Long> organizationIds = page.stream().map(IAccountDao.AccountUserInfo::getOrganizationId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        if (!organizationIds.isEmpty()) {
            organizationMap = organizationDao.findByIdInAndDeletedFalse(organizationIds).stream().collect(Collectors.toMap(OrganizationDO::getId, d -> d));
        }

        Map<Long, OrganizationDO> finalOrganizationMap = organizationMap;
        List<Long> existOrgIds = orgVipRecordDao.findRecordByDeletedFalse();

        return page.stream().filter(data -> !existOrgIds.contains(data.getOrganizationId())).map(data -> {
            AccountInfoVO res = new AccountInfoVO();
            res.setAccountId(data.getAccountId());
            res.setUsername(data.getUsername());
            res.setPhone(data.getPhone());
            if (finalOrganizationMap.containsKey(data.getOrganizationId())) {
                OrganizationDO organization = finalOrganizationMap.get(data.getOrganizationId());
                res.setType(organization.getType());
                res.setEnterName(organization.getEnterName());
            }
            return res;
        }).collect(Collectors.toList());
    }

    @Override
    public Boolean modifyAccount(Long accountId, ModifyAccountForm form) {
        AccountDO account = accountDao.findByIdAndDeletedFalse(accountId);
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(account), AccountExceptionEnum.ACCOUNT_NOT_EXIST);
        account.setUsername(form.getName());
        accountDao.save(account);
        return true;
    }

    @Override
    public OrgSimpleVO findOrgInfoByCreditCode(String creditCode) {
        OrganizationDO organization = organizationDao.findInfoByCreditCodeAndDeletedFalse(creditCode);
        if (organization != null) {
            Long organizationSourceId = organization.getOrganizationSourceId();
            OrganizationSourceDO organizationSource = organizationSourceDao.findInfoByIdAndDeletedFalse(organizationSourceId);
            OrgSimpleVO orgSimple = new OrgSimpleVO();
            orgSimple.setId(organization.getId());
            orgSimple.setName(organization.getEnterName());
            orgSimple.setType(organization.getType());
            orgSimple.setNeedPerAuthFile(organizationSource.getPerAuth());
            orgSimple.setNeedEntAuthFile(organizationSource.getEntAuth());
            return orgSimple;
        }
        return null;
    }

    @Override
    public List<AccountInfoVO> queryAliUserInfos(String userName) {

        List<AccountDO> accountInfos = accountDao.findInfosByUserNameAndOriginAndDeletedFalse(userName, EndpointType.ALIPAY_APPLET);
        List<AccountInfoVO> accountList = new ArrayList<>();
        for (AccountDO account : accountInfos) {
            AccountInfoVO accountInfo = new AccountInfoVO();
            accountInfo.setAccountId(account.getId());
            accountInfo.setUsername(account.getUsername());
            accountList.add(accountInfo);
        }
        return accountList;
    }

    @Override
    public BlackInfoVO queryUserIsBlack(String idNumber) {
        PersonRemoveDO personRemove = personRemoveDao.findInfoByIdNumber(idNumber);
        BlackInfoVO blackInfo = new BlackInfoVO();
        if (personRemove != null) {
            blackInfo.setIsBlack(true);
            blackInfo.setReason("不予公开，请与本人核实。");
        } else {
            blackInfo.setIsBlack(false);
            blackInfo.setReason("");
        }
        return blackInfo;
    }

    @Override
    public Boolean isShare(Long accountId) {
        List<InviteRecordDO> existInviteRecords = inviteRecordDao.findRecordByCreatorIdAndTypeAndDeletedFalse(accountId,
                InviteCodeType.INVITE);
        if (!CollectionUtils.isEmpty(existInviteRecords)) {
            return existInviteRecords.get(0).getIsPay() != null && existInviteRecords.get(0).getIsPay();
        } else {
            return false;
        }

    }

    @Override
    public String getPhoneInfo(String phoneCode) {
        UserPhoneInfoDTO userPhoneInfo = wxAppletApiService.getUserPhoneInfo(new GetUserInfoParam(phoneCode));
        return userPhoneInfo == null ? "" : userPhoneInfo.getPhoneInfo().getPhoneNumber();
    }

    @Override
    public Boolean isDivide(Long accountId) {
        List<InviteCodeDO> inviteCodeInfos = inviteCodeDao.findInfosByOperatorIdInAndTypeAndDeletedFalse(Collections.singletonList(accountId),
                InviteCodeType.DIVIDE_USE);
        return !CollectionUtils.isEmpty(inviteCodeInfos);
    }

    @Override
    public EnterSimpleInfoVO getEnterInfo(Long orgId) {

        OrganizationDO organization = organizationDao.findByIdAndDeletedFalse(orgId);
        AssertUtil.assertTrue(organization != null, OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);
        EnterSimpleInfoVO enterSimpleInfo = new EnterSimpleInfoVO();
        enterSimpleInfo.setOrgId(organization.getId());
        enterSimpleInfo.setEnterName(organization.getEnterName());
        enterSimpleInfo.setEnterPhone(organization.getPhone());
        enterSimpleInfo.setEnterNumber(organization.getIdNumber());
        enterSimpleInfo.setEnterAddr(organization.getEnterAddr());
        enterSimpleInfo.setCreditCode(organization.getCreditCode());
        enterSimpleInfo.setTaxpayerCode(organization.getTaxpayerCode());
        enterSimpleInfo.setLicense(organization.getLicense());
        return enterSimpleInfo;
    }

    @Override
    public Boolean updateEnterInfo(EnterSimpleInfoVO enterSimpleInfo) {
        String enterName = enterSimpleInfo.getEnterName();
        AssertUtil.assertTrue(!StringUtils.isEmpty(enterName), OrganizationExceptionEnum.ORGANIZATION_NAME_NOT_EXIST);
        List<OrganizationDO> organizationInfos = organizationDao.findOrgByEnterNameAndDeletedFalse(enterName);
        AssertUtil.assertTrue(CollectionUtils.isEmpty(organizationInfos), OrganizationExceptionEnum.ORGANIZATION_NAME_EXIST);
        OrganizationDO existOrg = organizationDao.findByIdAndDeletedFalse(enterSimpleInfo.getOrgId());
        AssertUtil.assertTrue(!ObjectUtils.isEmpty(existOrg), OrganizationExceptionEnum.ORGANIZATION_NOT_EXIST);
        existOrg.setId(enterSimpleInfo.getOrgId());
        existOrg.setEnterName(enterSimpleInfo.getEnterName());
        existOrg.setPhone(enterSimpleInfo.getEnterPhone());
        existOrg.setIdNumber(enterSimpleInfo.getEnterNumber());
        existOrg.setEnterAddr(enterSimpleInfo.getEnterAddr());
        existOrg.setCreditCode(enterSimpleInfo.getCreditCode());
        existOrg.setTaxpayerCode(enterSimpleInfo.getTaxpayerCode());
        existOrg.setLicense(enterSimpleInfo.getLicense());
        organizationDao.save(existOrg);
        return true;
    }

    @Override
    public AccountInfoDTO registerOrgUser(AccountWebRegisterVO webRegister) {

        String enterName = webRegister.getEnterName();
        AssertUtil.assertTrue(CollectionUtils.isEmpty(organizationDao.findOrgByEnterNameAndDeletedFalse(enterName)),
                OrganizationExceptionEnum.ORGANIZATION_NAME_EXIST);
        String phone = webRegister.getPhone();
        UserInfoDO userInfo = userInfoDao.findByPhoneAndDeletedFalse(phone);
        if (userInfo == null) {
            userInfo = new UserInfoDO();
            userInfo.setPhone(phone);
            userInfo.setCertNo(webRegister.getIdNumber());
            userInfo.setCertType("0");
            userInfo.setUserName(webRegister.getUsername());
            userInfoDao.save(userInfo);
        }
        OrganizationDO organization = new OrganizationDO();
        organization.setUsername(webRegister.getUsername());
        organization.setPhone(phone);
        organization.setIdNumber(webRegister.getIdNumber());
        organization.setType(OrganizationType.ENTER);
        organization.setEnterName(webRegister.getEnterName());
        organization.setAuthState(AuthState.PASS);
        organization.setState(SysOnOffState.ON);
        organization.setOrganizationSourceId(1L);
        organizationDao.save(organization);
        AccountDO account = new AccountDO();
        account.setOrganizationId(organization.getId());
        account.setUsername(webRegister.getUsername());
        account.setUserId(userInfo.getId());
        account.setType(UserType.PRIMARY);
        account.setOrigin(EndpointType.APPLET);
        account.setState(SysOnOffState.ON);
        account.setOpenId(IdUtil.fastSimpleUUID());
        account.setMembershipRankId(1L);
        accountDao.save(account);
        AccountInfoDTO accountInfo = new AccountInfoDTO();
        accountInfo.setUserId(userInfo.getId());
        accountInfo.setUsername(webRegister.getUsername());
        accountInfo.setAccountId(account.getId());
        accountInfo.setOrganizationId(organization.getId());
        accountInfo.setType(organization.getType());
        return accountInfo;
    }

}
